
#include "OutputStream.hpp"

libOutputStream::libOutputStream()
{
}

libOutputStream::~libOutputStream()
{
}

int libOutputStream::InitOutputStream(libIntelligentParser *_IP)
{
    if (NULL == _IP)
    {
        WriteLog("Failed assign Input_URL. INDEX_33");
        return -1;
    }
    IP = _IP;
    StartOutputStreamThread();

    return 0;
}

int libOutputStream::StartOutputStreamThread()
{
    thread t_output(Thread_OutputStream, this);
    t_output.detach();
    b_thread_flag = true;
    WriteLog("Output begin! INDEX_27");
    return 0;
}

void libOutputStream::Thread_OutputStream(void *p_this)
{
    libOutputStream *l = (libOutputStream*)p_this;
    return l->Thread_outputStreamm();
}

void libOutputStream::Thread_outputStreamm()
{
    int ret;

    int64_t in_video_frame_pts{ 0 }, in_audio_frame_pts{ 0 };
    int64_t video_frame_index{ 0 }, audio_frame_index{ 0 };

    int input_video_stream_index = IP->input_video_stream_index;
    int input_audio_stream_index = IP->input_audio_stream_index;
    AVRational input_video_stream_timebase = IP->input_video_stream_timebase;
    AVRational input_audio_stream_timebase = IP->input_audio_stream_timebase;
    // frame param
    AVCodecParameters *codecpar = IP->input_video_codecpar;
    int v_format = codecpar->format;
    int v_width = codecpar->width;
    int v_height = codecpar->height;

    codecpar = IP->input_audio_codecpar;
    int a_sample_format = codecpar->format;
    int a_nb_samples = codecpar->frame_size; //1024
    int a_channel = codecpar->channels;
    //avformatcontext
    string Output_URL264, Output_URL265; //可能需要多路输出 那么这里就比较不妥了
    bool b_264{ false }, b_265{ false };
    AVFormatContext *oc_264, *oc_265;
    AVCodecContextPP ctx_264, ctx_265, ctx_aac;
    //init codecctx
    //ctx_264.SetEncodecCtx(IP->v264_codecpar);
    //ctx_265.SetEncodecCtx(IP->v265_codecpar);  //error 另建的编码器无法链接上输出的端口  
    ctx_aac.SetEncodecCtx(IP->aac_codecpar);

    //h264    
    if (0 != IP->CI.OUT_264URL.length()) //
    {
        Output_URL264.assign(IP->CI.OUT_264URL);
        oc_264 = avformat_alloc_context();
        if (!oc_264)
        {
            WriteLog("Failed avformat_alloc_context . INDEX_99");
            return ;
        }
        if (Output_URL264.find("rtmp") == 0)
        {
            ret = avformat_alloc_output_context2(&oc_264, NULL, "flv", Output_URL264.c_str()); //输出到ffserver有异常
            if (ret < 0)
            {
                WriteLog("Failed avformat_alloc_output_context2 . INDEX_100");
                avformat_free_context(oc_264);
                return;
            }
        }
        else
        {
            ret = avformat_alloc_output_context2(&oc_264, NULL, /*"mpegts"*/NULL, Output_URL264.c_str()); //输出到ffserver有异常
            if (ret < 0)
            {
                WriteLog("Failed avformat_alloc_output_context2 . INDEX_100");
                avformat_free_context(oc_264);
                return;
            }
        }
        //add stream
        if (input_video_stream_index >= 0)
        {
            AVStream *stream_local;
            AVCodec *encodec = avcodec_find_encoder(IP->v264_codecpar->codec_id);
            stream_local = avformat_new_stream(oc_264, encodec);
            if (!stream_local)
            {
                WriteLog("Failed avformat_new_stream. INDEX_101");
                avformat_free_context(oc_264);
                return ;
            }
            stream_local->index = input_video_stream_index;
            stream_local->time_base = input_video_stream_timebase;
            stream_local->r_frame_rate = IP->input_videostream_rate; //使用输入时获得的数据 虽说常常输入流中 的参数都是错误的···

            ctx_264.codecctx= avcodec_alloc_context3(encodec);

            //明明是个废弃参数 为何有这么多需求是个需要研究的地方  无论是前置还是后置貌似都可以
            stream_local->codec = ctx_264.codecctx;//这里是个参数交换的要点位置 若不在这里设定好 则有导向ffserver时会有参数配置不齐的问题!!!!!!!!!!!!!!!!!!!!
            //视频的编码器似乎必须是在这里直接设置并启动  即使参数相同，也不能更换位置  有空翻翻源码看一下废弃函数是不是和现有函数有冲突还是怎么着

            if (avcodec_parameters_to_context(ctx_264.codecctx, IP->v264_codecpar)<0)
            {
                avcodec_free_context(&ctx_264.codecctx);
                return ;
            }
            ctx_264.codecctx->time_base = { 1,1000000 };
            ctx_264.codecctx->pix_fmt = (AVPixelFormat)IP->v264_codecpar->format;


            ctx_264.codecctx->codec = encodec;
            ctx_264.codecctx->codec_id = IP->v264_codecpar->codec_id;
            ctx_264.codecctx->codec_type = IP->v264_codecpar->codec_type;
            ctx_264.codecctx->ticks_per_frame = 2;

            ctx_264.codecctx->mb_lmin = 236;
            ctx_264.codecctx->mb_lmax = 3658;
//            ctx_264.codecctx->me_penalty_compensation = SI->VSI.me_penalty_compensation;
            ctx_264.codecctx->keyint_min = 25;
//            ctx_264.codecctx->scenechange_factor = SI->VSI.scenechange_factor;
            ctx_264.codecctx->mv0_threshold = 256;
//            ctx_264.codecctx->b_sensitivity = SI->VSI.b_sensitivity;
            ctx_264.codecctx->qmin = 2;
            ctx_264.codecctx->qmax = 51;
            ctx_264.codecctx->max_qdiff = 4;

            AVDictionary *param = 0;
            //av_dict_set(&param, "preset", "slow", 0);
            //av_dict_set(&param, "tune", "zerolatency", 0);//不用 对码率限制过多
            if (avcodec_open2(ctx_264.codecctx, encodec, &param) < 0)
            {
                WriteLog("Failed avcodec_open2 .\n");
                return;
            }

            ret = avcodec_parameters_from_context(stream_local->codecpar, ctx_264.codecctx);
            if (ret < 0) 
            {
                WriteLog("Failed avcodec_parameters_from_context. INDEX_333");
                return;
            }

            ctx_264.codecctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }
        if (input_audio_stream_index >= 0)
        {
            AVStream *stream_local = avformat_new_stream(oc_264, ctx_aac.codecctx->codec);
            if (!stream_local)
            {
                WriteLog("Failed avformat_new_stream. INDEX_103");
                avformat_free_context(oc_264);
                return ;
            }
            stream_local->index = input_audio_stream_index;
            stream_local->time_base = input_audio_stream_timebase; //?

            ret = avcodec_parameters_from_context(stream_local->codecpar, ctx_aac.codecctx);
            if (ret < 0) 
            {
                WriteLog("Failed avcodec_parameters_from_context. INDEX_334");
                return;
            }

            stream_local->codec = ctx_aac.codecctx;
            ctx_aac.codecctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }    
        av_dump_format(oc_264, 0, Output_URL264.c_str(), 1); //可以屏蔽

        if (!(oc_264->oformat->flags & AVFMT_NOFILE))
        {
            ret = avio_open(&oc_264->pb, Output_URL264.c_str(), AVIO_FLAG_WRITE);
            if (ret < 0)
            {
                WriteLog("Failed  avio_open(). INDEX_104");
                avformat_free_context(oc_264);
                return ;
            }
        }
        b_264 = true;
    }
    //h265
    if (0 != IP->CI.OUT_265URL.length()) //
    {
        Output_URL265.assign(IP->CI.OUT_265URL);
        oc_265 = avformat_alloc_context();
        if (!oc_265)
        {
            WriteLog("Failed avformat_alloc_context . INDEX_99");
            return;
        }

        if (Output_URL265.find("rtmp") == 0)
        {
            ret = avformat_alloc_output_context2(&oc_265, NULL, "flv", Output_URL265.c_str()); //265和flv不兼容啊
            if (ret < 0)
            {
                WriteLog("Failed avformat_alloc_output_context2 . INDEX_1003");
                avformat_free_context(oc_265);
                return;
            }
        }
        else
        {
            ret = avformat_alloc_output_context2(&oc_265, NULL, /*"mpegts"*/NULL, Output_URL265.c_str()); 
            if (ret < 0)
            {
                WriteLog("Failed avformat_alloc_output_context2 . INDEX_1003");
                avformat_free_context(oc_265);
                return;
            }
        }

        //add stream
        if (input_video_stream_index >= 0)
        {
            AVStream *stream_local;
            AVCodec *encodec = avcodec_find_encoder(IP->v265_codecpar->codec_id);
            stream_local = avformat_new_stream(oc_265, encodec);
            if (!stream_local)
            {
                WriteLog("Failed avformat_new_stream. INDEX_1016");
                avformat_free_context(oc_265);
                return;
            }
            stream_local->index = input_video_stream_index;
            stream_local->time_base = input_video_stream_timebase;
            stream_local->r_frame_rate = IP->input_videostream_rate; //使用输入时获得的数据 虽说常常输入流中 的参数都是错误的···

            ctx_265.codecctx = avcodec_alloc_context3(encodec);

            stream_local->codec = ctx_265.codecctx;//这里是个参数交换的要点位置 若不在这里设定好 则有导向ffserver时会有参数配置不齐的问题!!!!!!!!!!!!!!!!!!!!
//            ctx_265.codecctx->flags |= CODEC_FLAG_GLOBAL_HEADER;//没用吗？

            if (avcodec_parameters_to_context(ctx_265.codecctx, IP->v265_codecpar) < 0)
            {
                avcodec_free_context(&ctx_265.codecctx);
                return;
            }
            ctx_265.codecctx->time_base = { 1,1000000 };
            ctx_265.codecctx->pix_fmt = (AVPixelFormat)IP->v265_codecpar->format;


            ctx_265.codecctx->codec = encodec;
            ctx_265.codecctx->codec_id = IP->v265_codecpar->codec_id;
            ctx_265.codecctx->codec_type = IP->v265_codecpar->codec_type;
            ctx_265.codecctx->ticks_per_frame = 1;//h265

            ctx_265.codecctx->mb_lmin = 236;
            ctx_265.codecctx->mb_lmax = 3658;
//            ctx_265.codecctx->me_penalty_compensation = SI->VSI.me_penalty_compensation;
            ctx_265.codecctx->keyint_min = 25;
//            ctx_265.codecctx->scenechange_factor = SI->VSI.scenechange_factor;
            ctx_265.codecctx->mv0_threshold = 256;
//            ctx_265.codecctx->b_sensitivity = SI->VSI.b_sensitivity;
            ctx_265.codecctx->qmin = 2;
            ctx_265.codecctx->qmax = 51;
//            ctx_265.codecctx->max_qdiff = 4;
//            ctx_265.codecctx->gop_size = 30;
//            ctx_265.codecctx->bit_rate_tolerance = 1000;
//            ctx_265.codecctx->max_b_frames = 3;

            AVDictionary *param = 0;
            av_dict_set(&param, "x265-params", "qp=20", 0);
            av_dict_set(&param, "preset", "ultrafast", 0);
            av_dict_set(&param, "tune", "zero-latency", 0);
            if (avcodec_open2(ctx_265.codecctx, encodec, &param) < 0)
            {
                WriteLog("Failed avcodec_open2. INDEX_3339\n");
                return;
            }

            ret = avcodec_parameters_from_context(stream_local->codecpar, ctx_265.codecctx);
            if (ret < 0)
            {
                WriteLog("Failed avcodec_parameters_from_context. INDEX_3336");
                return;
            }
            if (oc_265->oformat->flags&AVFMT_GLOBALHEADER)
            {
                ctx_265.codecctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
            }            
            ctx_265.codecctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }
        if (input_audio_stream_index >= 0)
        {
            AVStream *stream_local = avformat_new_stream(oc_265, ctx_aac.codecctx->codec);
            if (!stream_local)
            {
                WriteLog("Failed avformat_new_stream. INDEX_1035");
                avformat_free_context(oc_265);
                return;
            }
            stream_local->index = input_audio_stream_index;
            stream_local->time_base = input_audio_stream_timebase; //?

            ret = avcodec_parameters_from_context(stream_local->codecpar, ctx_aac.codecctx);
            if (ret < 0)
            {
                WriteLog("Failed avcodec_parameters_from_context. INDEX_3345");
                return;
            }

            stream_local->codec = ctx_aac.codecctx;
            ctx_aac.codecctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }

        av_dump_format(oc_265, 0, Output_URL265.c_str(), 1); //可以屏蔽

        if (!(oc_265->oformat->flags & AVFMT_NOFILE))
        {
            ret = avio_open(&oc_265->pb, Output_URL265.c_str(), AVIO_FLAG_WRITE);
            if (ret < 0)
            {
                WriteLog("Failed  avio_open(). INDEX_10497");
                avformat_free_context(oc_265);
                return;
            }
        }
        b_265 = true;
    }
    //
    if (b_264)
    {
        oc_264->oformat->video_codec = ctx_264.codecctx->codec_id;
        oc_264->oformat->audio_codec = ctx_aac.codecctx->codec_id;
        ret = avformat_write_header(oc_264, NULL); //返回-22源于stream中的参数缺失 内部函数若需要可以多深入一些
        if (ret < 0)
        {
            WriteLog("Failed write header 264. INDEX_300");
            return;
        }
    }
    if (b_265)//
    {
        oc_265->oformat->video_codec = ctx_265.codecctx->codec_id;
        oc_265->oformat->audio_codec = ctx_aac.codecctx->codec_id;
        ret = avformat_write_header(oc_265, NULL); //返回-22源于stream中的参数缺失 内部函数若需要可以多深入一些
        if (ret < 0)
        {
            WriteLog("Failed write header 265. INDEX_3001");
            return;
        }
    }
    AVPacketPP packet;
    while (true)
    {        
        b_thread_flag = true;
        if (IP->GetVideoBufferPercent() < 80)
        {
            continue;
        }
//        cout << "video " << in_video_frame_pts << "  audio " << in_audio_frame_pts << endl;
        if (av_compare_ts(in_audio_frame_pts, input_audio_stream_timebase, in_video_frame_pts, input_video_stream_timebase) > 0)//audio first
        {
            AVFramePP frame_video;
            ret = IP->ReadVideoFrame(frame_video, video_frame_index);
            if (ret != 0)
            {
                WriteLog("Failed ReadVideoFrame. INDEX_204");
                continue;
            }
            in_video_frame_pts = frame_video.frame->pts;
            //
            if (b_264)
            {
                frame_video.frame->pts = av_rescale_q_rnd(in_video_frame_pts, input_video_stream_timebase, ctx_264.codecctx->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                frame_video.frame->pict_type = AV_PICTURE_TYPE_NONE;
                ret = ctx_264.EncodecFrame(frame_video.frame, packet.packet);
                //if (ctx_264.EncodecFrame(frame_video.frame, packet.packet) != 0)
                //{
                //    packet.unref();
                //    continue;
                //}
                if (ret == 0)
                {
                    packet.packet->stream_index = input_video_stream_index;
                    packet.packet->pts = av_rescale_q_rnd(packet.packet->pts, ctx_264.codecctx->time_base, oc_264->streams[input_video_stream_index]->time_base, 
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->dts = av_rescale_q_rnd(packet.packet->dts, ctx_264.codecctx->time_base, oc_264->streams[input_video_stream_index]->time_base, 
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    //packet.packet->duration = av_rescale_q_rnd(33333, in_v_stream_timebase, oc_264->streams[in_v_stream_index]->time_base,
                    //    (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->pos = -1;
                    ret = av_interleaved_write_frame(oc_264, packet.packet);
                    if (0 != ret)
                    {
                        WriteLog("Failed av_interleaved_write_frame video. INDEX_5120");
                        packet.unref();
                        continue;
                    }
                }
            }
            if (b_265)
            {
                frame_video.frame->pts = av_rescale_q_rnd(in_video_frame_pts, input_video_stream_timebase, ctx_265.codecctx->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                frame_video.frame->pict_type = AV_PICTURE_TYPE_NONE;
                ret = ctx_265.EncodecFrame(frame_video.frame, packet.packet);
                //if (ctx_265.EncodecFrame(frame_video.frame, packet.packet) != 0)
                //{
                //    packet.unref();
                //    continue;
                //}
                if (ret == 0)
                {
                    packet.packet->stream_index = input_video_stream_index;
                    packet.packet->pts = av_rescale_q_rnd(packet.packet->pts, ctx_265.codecctx->time_base, oc_265->streams[input_video_stream_index]->time_base,
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->dts = av_rescale_q_rnd(packet.packet->dts, ctx_265.codecctx->time_base, oc_265->streams[input_video_stream_index]->time_base,
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    //packet.packet->duration = av_rescale_q_rnd(packet.packet->duration, in_v_stream_timebase, oc_265->streams[in_v_stream_index]->time_base,
                    //    (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->pos = -1;
                    ret = av_interleaved_write_frame(oc_265, packet.packet);
                    if (0 != ret)
                    {
                        WriteLog("Failed av_interleaved_write_frame video. INDEX_5120");
                        packet.unref();
                        continue;
                    }
                }
            }
        }
        else
        {
            AVFramePP frame_audio;
            ret = IP->ReadAudioFrame(frame_audio);
            if (ret!=0)
            {
                WriteLog("Failed ReadAudioFrame. INDEX_203");
                continue;
            }
            in_audio_frame_pts = frame_audio.frame->pts;

            frame_audio.frame->pts = av_rescale_q_rnd(frame_audio.frame->pts, input_audio_stream_timebase, ctx_aac.codecctx->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            if (ctx_aac.EncodecFrame(frame_audio.frame, packet.packet) != 0)
            {
                packet.unref();
                continue;
            }
            else //获得包
            {
                packet.packet->stream_index = input_audio_stream_index;
                if (b_264)
                {
                    packet.packet->pts = av_rescale_q_rnd(packet.packet->pts, ctx_aac.codecctx->time_base, oc_264->streams[input_audio_stream_index]->time_base, 
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->dts = av_rescale_q_rnd(packet.packet->dts, ctx_aac.codecctx->time_base, oc_264->streams[input_audio_stream_index]->time_base, 
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->duration = av_rescale_q_rnd(a_nb_samples, ctx_aac.codecctx->time_base, oc_264->streams[input_audio_stream_index]->time_base,
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->pos = -1;
                    ret = av_interleaved_write_frame(oc_264, packet.packet);
                    if (0 != ret)
                    {
                        packet.unref();
                        WriteLog("Failed av_interleaved_write_frame audio. INDEX_120");
                        continue;
                    }
                }
                if (b_265)
                {
                    packet.packet->pts = av_rescale_q_rnd(packet.packet->pts, ctx_aac.codecctx->time_base, oc_265->streams[input_audio_stream_index]->time_base,
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->dts = av_rescale_q_rnd(packet.packet->dts, ctx_aac.codecctx->time_base, oc_265->streams[input_audio_stream_index]->time_base,
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->duration = av_rescale_q_rnd(a_nb_samples, ctx_aac.codecctx->time_base, oc_265->streams[input_audio_stream_index]->time_base,
                        (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                    packet.packet->pos = -1;
                    ret = av_interleaved_write_frame(oc_265, packet.packet);
                    if (0 != ret)
                    {
                        packet.unref();
                        WriteLog("Failed av_interleaved_write_frame audio. INDEX_120");
                        continue;
                    }
                }
            }
        }
        packet.unref();
    }
    if (b_264)
    {
        avformat_flush(oc_264);
        avformat_close_input(&oc_264); //内含清理
    }
    if (b_265)
    {
        avformat_flush(oc_265);
        avformat_close_input(&oc_265); 
    }
    b_thread_flag = false;
    WriteTXT("State.in", "stop");
}
